Exercice sur l'ACP

Rappel : le but de l'ACP est de trouver l'espace de représentation (engendré par les composantes principales ) qui représentent le mieux le nuage de points ( = les observations).

In [1]:
import numpy as np 
import csv
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
sns.set(color_codes = True)
from data import *
from scipy import stats
from sklearn.decomposition import PCA
In [2]:
X1 = [3, 6, 4, 2, -1, 1]
X2 = [2, 4, 3, 3, 1, 1] 

X = np.zeros((6, 2))
X[:,0] = X1.copy()
X[:,1] = X2.copy()

print('Données brutes : \n', X)
Données brutes : 
 [[ 3.  2.]
 [ 6.  4.]
 [ 4.  3.]
 [ 2.  3.]
 [-1.  1.]
 [ 1.  1.]]
In [3]:
plt.figure(figsize = (9,7))
plt.plot(X1, X2 , 's')
plt.axis([-2,7, -2, 5])
plt.axvline(linewidth=2, color='blue')
plt.axhline(linewidth=2, color='blue')
#plt.legend(['données brutes'], fontsize = 14)
plt.title('Données brutes', fontsize = 20)
plt.xticks(fontsize = 18)
plt.yticks(fontsize = 18)
plt.xlabel('X1',fontsize = 18)
plt.ylabel('X2',fontsize = 18)
plt.show()
In [4]:
# Moyenne
m1 = np.mean(X1)
m2 = np.mean(X2)

print('Moyenne de X1 : ' + str(m1) + ', et moyenne de X2 :' + str(m2))

# Ecart-type
sig1 = np.std(X1)
sig2 = np.std(X2)

print('Ecart-type de X1 : ' + str(sig1) + ', et écart-type de X2 :' + str(sig2))
Moyenne de X1 : 2.5, et moyenne de X2 :2.33333333333
Ecart-type de X1 : 2.21735578261, et écart-type de X2 :1.10554159679

Changement de repère après centrage des données

In [5]:
plt.figure(figsize = (9,7))
plt.plot(X1, X2 , 's')
plt.axvline(linewidth=2, color='blue')
plt.axhline(linewidth=2, color='blue')
plt.axis([-2,7, -2, 5])
plt.axvline(x = m1, linewidth=2, color='r')
plt.axhline(y = m2, linewidth=2, color='r')
plt.plot(m1, m2 , 'or')
plt.xticks(fontsize = 18)
plt.yticks(fontsize = 18)
plt.xlabel('X1',fontsize = 18)
plt.ylabel('X2',fontsize = 18)
plt.title('Représentation du centre du nuage de points' , fontsize = 20)
plt.show()
In [6]:
plt.figure(figsize = (9,7))
plt.plot(X1-m1, X2-m2 , 'o')
plt.axvline(linewidth=2, color='r')
plt.axhline(linewidth=2, color='r')
plt.axis([-4,5, -2, 5])
plt.title('Centrage du nuage de points', fontsize = 20)
plt.legend(['données brutes centrées'], fontsize = 18)
plt.xticks(fontsize = 18)
plt.yticks(fontsize = 18)
plt.xlabel('X1 - $m_1$',fontsize = 18)
plt.ylabel('X2 - $m_2$',fontsize = 18)
plt.show()

Réduction des données centrées

In [7]:
Xtilde = np.zeros((6, 2))
Xtilde[:,0] = (X1.copy() - m1)/sig1
Xtilde[:,1] = (X2.copy() - m2)/sig2

print('Données centrées et réduites : \n', Xtilde)
Données centrées et réduites : 
 [[ 0.22549381 -0.30151134]
 [ 1.57845666  1.50755672]
 [ 0.67648143  0.60302269]
 [-0.22549381  0.60302269]
 [-1.57845666 -1.20604538]
 [-0.67648143 -1.20604538]]
In [8]:
plt.figure(figsize = (9,7))
plt.plot(X1-m1, X2-m2 , 'o')
plt.plot(Xtilde[:,0], Xtilde[:,1], '*r' , markersize = 20 )
plt.axvline( linewidth=2, color='r')
plt.axhline( linewidth=2, color='r')
plt.axis([-4,5, -2, 5])
plt.title('Effet de la réduction sur le nuage de points centré', fontsize = 20)
plt.legend(['données brutes centrées', 'données centrées réduites'], fontsize = 18)
plt.xticks(fontsize = 18)
plt.yticks(fontsize = 18)
plt.xlabel('X1 - $m_1$',fontsize = 18)
plt.ylabel('X2 - $m_2$',fontsize = 18)
plt.show()

Matrice de covariance des données centrées-réduites

In [10]:
C = np.dot(Xtilde.T, Xtilde)/6.
C
Out[10]:
array([[ 1.        ,  0.88385624],
       [ 0.88385624,  1.        ]])
In [11]:
acp = PCA(2) # Initialisation de l'objet PCA (qui va calculer les différentes étapes de l'ACP)
acp.fit(Xtilde) # Calcul des différentes étapes de l'ACP de Xtilde
Out[11]:
PCA(copy=True, iterated_power='auto', n_components=2, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)
In [12]:
acp.explained_variance_  # Valeurs propres ordonnées (décroissant)
Out[12]:
array([ 1.88385624,  0.11614376])
In [13]:
acp.explained_variance_ratio_
Out[13]:
array([ 0.94192812,  0.05807188])

Les composantes principales sont les deux nouvelles variables U1 et U2 portées par les axes u1 et u2 ci-dessous. La variance portée par la première composante principale u1 représente 94% de l'information totale contenue dans les variables. Ce qui veut dire qu'à elle seule, la variable U1 décrit relativement bien les données.

Vecteurs propres

In [14]:
u1 = acp.components_[0] 
print(u1)
[ 0.70710678  0.70710678]
In [15]:
u2 = acp.components_[1] 
print(u2)
[-0.70710678  0.70710678]

Représentation des vecteurs propres

In [17]:
plt.figure(figsize = (9,7))
plt.plot(Xtilde[:,0], Xtilde[:,1], '*r' , markersize = 20)
plt.axvline( linewidth=2, color='r')
plt.axhline( linewidth=2, color='r')

plt.plot([-2*acp.components_[0][0] ,3*acp.components_[0][0] ],[-2*acp.components_[0][1],3*acp.components_[0][1] ], 'gray') # Premier vecteur propre
plt.plot([-acp.components_[1][0],acp.components_[1][0] ],[-acp.components_[1][1],acp.components_[1][1] ], 'gray') # Second vecteur propre
plt.plot([0,acp.components_[0][0] ],[0,acp.components_[0][1] ], linewidth=3) # Premier vecteur propre
plt.plot([0,acp.components_[1][0] ],[0,acp.components_[1][1] ],linewidth=3) # Second vecteur propre
plt.axis([-2, 2.5, -1.5, 2])
plt.xlabel('$\~X_1$', fontsize = 18)
plt.ylabel('$\~X_2$', fontsize = 18)
plt.xticks(fontsize = 18)
plt.yticks(fontsize = 18)
plt.title('Données centrées et réduites, u1 en bleu, u2 en vert', fontsize = 20)
plt.show()

Représentation des points dans le nouvel espace

In [19]:
Xproj = acp.transform(Xtilde)
print(Xproj)
[[-0.05375252 -0.37264892]
 [ 2.18214099 -0.05013383]
 [ 0.90474604 -0.05194317]
 [ 0.26695323  0.58584963]
 [-1.96894027  0.26333454]
 [-1.33114747 -0.37445826]]
In [21]:
plt.figure(figsize = (9,7))
plt.axvline( linewidth=2, color='gray')
plt.axhline( linewidth=2, color='gray')
plt.plot([0,1], [0,0],linewidth=3)
plt.plot([0,0], [0,1],linewidth=3)
plt.plot(Xproj[:,0], Xproj[:,1], '*r' , markersize = 20)
plt.axis([-2, 2.5, -1.5, 2])
plt.xlabel('$U_1$', fontsize = 18)
plt.ylabel('$U_2$', fontsize = 18)
plt.xticks(fontsize = 18)
plt.yticks(fontsize = 18)
plt.title('Données projetées sur u1 et u2', fontsize = 20)
plt.show()

Ratio de la variance portée par l'axe $U_1$

In [22]:
np.var(Xproj[:,0])
Out[22]:
1.883856237743706
In [23]:
np.var(Xproj[:,0])/(np.var(Xproj[:,0]) + np.var(Xproj[:,1]))
Out[23]:
0.94192811887185324

Ratio de la variance portée par l'axe $U_2$

In [24]:
np.var(Xproj[:,1])
Out[24]:
0.1161437622562936
In [25]:
np.var(Xproj[:,1])/(np.var(Xproj[:,0]) + np.var(Xproj[:,1]))
Out[25]:
0.058071881128146811

Pour comparaison, ratio de la variance portée par les axes $X_1$ et $X_2$

In [26]:
np.var(X[:,0])/(np.var(X[:,0]) + np.var(X[:,1]))
Out[26]:
0.80090497737556565
In [27]:
np.var(X[:,1])/(np.var(X[:,0]) + np.var(X[:,1]))
Out[27]:
0.1990950226244344

$\rightarrow$ on a bien maximiser la variance portée par le premier axe en changeant d'espace de représentation

Pour aller plus loin - Exercice en dimension 3

In [28]:
N = 30

X1 = np.random.randint(-10,10, N)
X2 = np.random.randint(-2,15, N)
X3 = 2*X1 -3*X2 + 4 + np.random.normal(0, 1.2, N) # Relation quasi linéaire entre X3 et les variables X1 et X2 (au bruit près)
In [29]:
from mpl_toolkits.mplot3d import Axes3D


fig = plt.figure()
ax = Axes3D(fig)


ax.scatter(X1,X2, X3)
ax.set_xlabel('X1')
ax.set_ylabel('X2')
ax.set_zlabel('X3')
plt.show()
In [30]:
# Calcul de la moyenne
m1 = np.mean(X1)
m2 = np.mean(X2)
m3 = np.mean(X3)

# Calcul de l'écart-type
sig1 = np.std(X1)
sig2 = np.std(X2)
sig3 = np.std(X3)

# Données centrées et réduites
Xtilde = np.zeros((N, 3))
Xtilde[:,0] = (X1 - m1)/sig1
Xtilde[:,1] = (X2 - m2)/sig2
Xtilde[:,2] = (X3 - m3)/sig3

Matrice de covariance de X

In [31]:
C3D = np.dot(Xtilde.T, Xtilde)/N
print(C3D)
[[ 1.          0.15952704  0.50164795]
 [ 0.15952704  1.         -0.77239792]
 [ 0.50164795 -0.77239792  1.        ]]

ACP

In [32]:
acp3 = PCA(3)
acp3.fit(Xtilde)
Out[32]:
PCA(copy=True, iterated_power='auto', n_components=3, random_state=None,
  svd_solver='auto', tol=0.0, whiten=False)

Valeurs propres

In [34]:
acp3.explained_variance_
Out[34]:
array([  1.85375475e+00,   1.14498380e+00,   1.26144527e-03])
In [35]:
np.cumsum(acp.explained_variance_ratio_)
Out[35]:
array([ 0.94192812,  1.        ])

Projection dans l'espace des composantes principales

In [36]:
Xproj3D = acp3.transform(Xtilde)

fig = plt.figure()
ax = Axes3D(fig)


ax.scatter(Xproj3D[:,0],Xproj3D[:,1],Xproj3D[:,2])
ax.set_xlabel('U1')
ax.set_ylabel('U2')
ax.set_zlabel('U3')
plt.show()

Représentation sur les axes $U_1$ et $U_2$

In [37]:
plt.figure(figsize = (6,6))
plt.axvline( linewidth=2, color='gray')
plt.axhline( linewidth=2, color='gray')
plt.plot([0,1], [0,0],linewidth=3)
plt.plot([0,0], [0,1],linewidth=3)
plt.plot(Xproj3D[:,0], Xproj3D[:,1], 'o')
plt.xlabel('$U_1$', fontsize = 14)
plt.ylabel('$U_2$', fontsize = 14)
plt.axis([-3, 3, -3,3])
plt.show()

Représentation sur les axes $U_1$ et $U_2$

In [38]:
plt.figure(figsize = (6,6))
plt.axvline( linewidth=2, color='gray')
plt.axhline( linewidth=2, color='gray')
plt.plot([0,1], [0,0],linewidth=3)
plt.plot([0,0], [0,1],linewidth=3)
plt.plot(Xproj3D[:,1], Xproj3D[:,2], 'o')
plt.xlabel('$U_2$', fontsize = 14)
plt.ylabel('$U_3$', fontsize = 14)
plt.axis([-3, 3, -3, 3])
plt.show()

Représentation sur les axes $U_1$ et $U_3$

In [39]:
plt.figure(figsize = (6, 6))
plt.axvline( linewidth=2, color='gray')
plt.axhline( linewidth=2, color='gray')
plt.plot([0,1], [0,0],linewidth=3)
plt.plot([0,0], [0,1],linewidth=3)
plt.plot(Xproj3D[:,0], Xproj3D[:,2], 'o')
plt.xlabel('$U_1$', fontsize = 14)
plt.ylabel('$U_3$', fontsize = 14)
plt.axis([-3, 3, -3, 3])
plt.show()

Réduction de dimension p = 3 $\rightarrow$ 2, soit 99, 88% de la variance expliquée calculée directement

In [40]:
acp2 = PCA(2)
acp2.fit(Xtilde)
acp2.explained_variance_
Out[40]:
array([ 1.85375475,  1.1449838 ])
In [41]:
np.cumsum(acp2.explained_variance_ratio_)
Out[41]:
array([ 0.61791825,  0.99957952])
In [42]:
Xproj2 = acp2.transform(Xtilde)
In [43]:
plt.figure(figsize = (6,6))
plt.axvline( linewidth=2, color='gray')
plt.axhline( linewidth=2, color='gray')
plt.plot([0,1], [0,0],linewidth=3)
plt.plot([0,0], [0,1],linewidth=3)
plt.plot(Xproj2[:,0], Xproj2[:,1], 'o')
plt.xlabel('$U_1$', fontsize = 14)
plt.ylabel('$U_2$', fontsize = 14)
plt.axis([-3, 3, -3, 3])
plt.show()